package com.alogic.sdep.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.anysoft.webloader.HttpClientTool;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.alogic.auth.CookieManager;
import com.alogic.auth.Session;
import com.alogic.auth.SessionManager;
import com.alogic.auth.SessionManagerFactory;
import com.alogic.sdep.SdepConstants;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.Script;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.doc.json.JsonObject;
import com.anysoft.util.BaseException;
import com.anysoft.util.Configurable;
import com.anysoft.util.IOTools;
import com.anysoft.util.KeyGen;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.Settings;
import com.anysoft.util.XMLConfigurable;
import com.anysoft.util.XmlElementProperties;
import com.anysoft.util.XmlTools;
import com.anysoft.util.resource.ResourceFactory;
import com.anysoft.webloader.ServletConfigProperties;
import com.anysoft.webloader.ServletHandler;
import com.logicbus.backend.Context;
import com.logicbus.backend.bizlog.BizLog;
import com.logicbus.backend.server.http.HttpCacheTool;
import com.logicbus.backend.server.http.HttpContext;

/**
 * SEDP服务端处理器
 * 
 * @author duanyy
 * @since 1.6.12.36 [20190611 duanyy] <br>
 */
public class SdepHandler implements ServletHandler,XMLConfigurable,Configurable,SdepConstants{
	/**
	 * a logger of slf4j
	 */
	protected final static Logger LOG = LoggerFactory.getLogger(SdepHandler.class);
	
	/**
	 * 缺省配置文件
	 */
	protected static final String DEFAULT = "java:///conf/alogic.sdep.server.xml#App";
	
	/**
	 * keyId参数名
	 */
	protected String arguKeyId = ARGU_KEYID;
	
	/**
	 * signature参数名
	 */
	protected String arguSignature = ARGU_SIGNATURE;
	
	/**
	 * service参数
	 */
	protected String arguService = ARGU_SERVICE;
	
	/**
	 * 编码
	 */
	protected String encoding = "utf-8";	
	
	/**
	 * command的前缀
	 */
	protected String cmdPrefix = "/sedp";
	
	/**
	 * 内部跳转URL的参数
	 */
	protected String returnURL = ARGU_RETURNURL;	
	
	protected String sessionGroup = "$sdep-server";	
	
	/**
	 * 本服务器的登录地址
	 */
	protected String loginURL = "/login";
	
	/**
	 * 当登录时执行脚本
	 */
	protected Logiclet onLogin = null;	
	
	/**
	 * 当登录时执行脚本
	 */
	protected Logiclet onNoLogin = null;		
	
	protected String contentType = "text/html;charset=utf-8";
	
	protected HttpClientTool httpClientTool = null;
	
	protected HttpCacheTool cacheTool = null;
	
	public void bizlog(String service,String clientIp,long time,long duration,String url,boolean error,String reason){
		BizLog.log(KeyGen.uuid(8,0,15),service,clientIp,error?CODE_ERR:CODE_OK,reason,time,duration,url);
	}
	
	@Override
	public void configure(Properties props) {
		cmdPrefix = PropertiesConstants.getString(props, "cmdPrefix",cmdPrefix);
		returnURL = PropertiesConstants.getString(props,"auth.para.url",returnURL);
		loginURL = PropertiesConstants.getString(props, "auth.page.login", loginURL);
		arguKeyId = PropertiesConstants.getString(props, "sdep.para.keyId", arguKeyId);
		arguService = PropertiesConstants.getString(props, "sdep.para.service", arguService);
		arguSignature = PropertiesConstants.getString(props, "sdep.para.signature",arguSignature);
		encoding = PropertiesConstants.getString(props,"http.encoding",encoding);		
		sessionGroup = PropertiesConstants.getString(props, "sdep.server.group",sessionGroup);	
		httpClientTool = Settings.getToolkit(HttpClientTool.class);
		cacheTool = Settings.getToolkit(HttpCacheTool.class);
	}

	@Override
	public void configure(Element e, Properties p) {
		Properties props = new XmlElementProperties(e,p);
		configure(props);		
		
		Element elem  = XmlTools.getFirstElementByPath(e, "on-login");
		if (elem != null){
			onLogin = Script.create(elem, props);
		}	
		elem  = XmlTools.getFirstElementByPath(e, "on-nologin");
		if (elem != null){
			onNoLogin = Script.create(elem, props);
		}			
	}

	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		ServletConfigProperties props = new ServletConfigProperties(servletConfig);
		String master = PropertiesConstants.getString(props, "sdep.server.master", DEFAULT);
		String secondary = PropertiesConstants.getString(props, "sdep.server.secondary", DEFAULT);
		ResourceFactory rf = Settings.getResourceFactory();

		InputStream in = null;
		try {
			in = rf.load(master, secondary, null);
			Document doc = XmlTools.loadFromInputStream(in);
			if (doc != null){
				configure(doc.getDocumentElement(), props);
			}
		}catch (Exception ex){
			LOG.error("Can not init gateway with file : " + master);
		}finally{
			IOTools.close(in);
		}	
	}

	@Override
	public void doService(HttpServletRequest httpReq,HttpServletResponse httpResp,String method)throws ServletException, IOException {
		SessionManager sm = SessionManagerFactory.getDefault();
		Session session = sm.getSession(httpReq,httpResp,true);
		
		try {
			doLogin(httpReq,httpResp,sm,session);
		}catch (BaseException ex){
			httpClientTool.sendError(httpResp,E404,String.format("%s:%s",ex.getCode(),ex.getMessage()));
		}
	}

	protected void doLogin(HttpServletRequest httpReq,
			HttpServletResponse httpResp, SessionManager sm, Session session) {
		if (!session.isLoggedIn()){			
			String nextUrl = loginURL;
			try {
				String returnUrl = httpReq.getRequestURI();
				String queryInfo = httpReq.getQueryString();
				if (StringUtils.isNotEmpty(queryInfo)){
					returnUrl += "?" + queryInfo;
				}
				if (nextUrl.indexOf("?") >= 0){
					nextUrl += String.format("&%s=%s", this.returnURL,URLEncoder.encode(returnUrl,encoding));
				}else{
					nextUrl += String.format("?%s=%s", this.returnURL,URLEncoder.encode(returnUrl,encoding));
				}
			}catch (Exception ex){
				LOG.error(ex.getMessage());
			}
			
			if (onNoLogin != null){
				Context ctx = new HttpContext(httpReq,httpResp,encoding);		
				LogicletContext logicletContext = new Context.ServantLogicletContext(ctx);	
				CookieManager cm = new CookieManager.Default(sm, httpReq, httpResp);
				long start = System.nanoTime();
				String clientIp = httpClientTool.getClientIp(httpReq);
				try {
					logicletContext.setObject(ID_SESSION, session);		
					logicletContext.setObject(ID_COOKIES, cm) ;
					logicletContext.SetValue("$service", "/sdep/NoLogin");
					logicletContext.SetValue("$nextUrl", nextUrl);
					logicletContext.SetValue("$clientIp",clientIp);			
					
					XsObject doc = new JsonObject("root",new HashMap<String,Object>());
					onNoLogin.execute(doc,doc, logicletContext, null);
					nextUrl = PropertiesConstants.getString(logicletContext,"$nextUrl",nextUrl);
				}finally{
					logicletContext.removeObject(ID_SESSION);
					logicletContext.removeObject(ID_COOKIES);
					bizlog("/sdep/NoLogin",clientIp,System.currentTimeMillis(),System.nanoTime() - start,httpReq.getRequestURL().toString(),false,"");
				}
			}
			try {
				httpClientTool.sendRedirect(httpResp,nextUrl);
			}catch (Exception ex){
				LOG.error(ex.getMessage());
			}
		}else{			
			String content = "";
			String type = contentType;
			String nextUrl = loginURL;
			if (onLogin != null){				
				Context ctx = new HttpContext(httpReq,httpResp,encoding);		
				LogicletContext logicletContext = new Context.ServantLogicletContext(ctx);	
				CookieManager cm = new CookieManager.Default(sm, httpReq, httpResp);
				long start = System.nanoTime();
				String clientIp = httpClientTool.getClientIp(httpReq);				
				try {
					logicletContext.setObject(ID_SESSION, session);		
					logicletContext.setObject(ID_COOKIES, cm) ;
					logicletContext.SetValue("$service", "/sdep/Login");
					logicletContext.SetValue("$clientIp",clientIp);			
					logicletContext.SetValue("$nextUrl", nextUrl);
					
					XsObject doc = new JsonObject("root",new HashMap<String,Object>());
					onLogin.execute(doc,doc, logicletContext, null);
					content = PropertiesConstants.getString(logicletContext,"$htmlContent",content);
					type = PropertiesConstants.getString(logicletContext,"$htmlContentType",type);
					nextUrl = PropertiesConstants.getString(logicletContext,"$nextUrl",nextUrl);
				}finally{
					logicletContext.removeObject(ID_SESSION);
					logicletContext.removeObject(ID_COOKIES);
					bizlog("/sdep/NoLogin",clientIp,System.currentTimeMillis(),System.nanoTime() - start,httpReq.getRequestURL().toString(),false,"");
				}
			}
			if (StringUtils.isNotEmpty(content)){
				cacheTool.cacheDisable(httpResp);
				httpResp.setContentType(type);
				
				OutputStream out = null;
				try {
					out = httpResp.getOutputStream();
					out.write(content.getBytes(encoding));
				}catch (IOException ex){
					LOG.error(ex.getMessage());
				}finally{
					IOTools.close(out);
				}
			}else{
				try {
					httpClientTool.sendRedirect(httpResp,nextUrl);
				} catch (IOException e) {
					LOG.error(e.getMessage());
				}
			}
		}
	}
	

	@Override
	public void destroy() {

	}
	
	/**
	 * 通过URI计算出当前的command
	 * @param uri URI
	 * @return cmd
	 */
	protected String getCommand(String contextPath,String uri){
		String prefix = contextPath + this.cmdPrefix;
		if (uri.startsWith(prefix)){
			return uri.substring(prefix.length());
		}else{
			return "";
		}
	}
	
	protected String getParameter(HttpServletRequest request,String id,String dftValue){
		String value = request.getParameter(id);
		return StringUtils.isEmpty(value)?dftValue:value;
	}	
}